#ifndef FILE_H
#define FILE_H

/**
  An enum specifying all the possible types of file.
  A File* object should respond with one of these
  to verify what type of File it is.
**/
enum FileTypes
{
  eFileType_File,       // Pure virtual, should never be used.
  eFileType_Directory,
  eFileType_CharDevice,
  eFileType_BlockDevice,
  eFileType_Symlink,
  eFileType_RegularFile
};

#include <Globals.h>
#include <Resource.h>
#include <FileDescriptor.h>
#include <SharedObject.h>
#include <ProcessManager.h>
#include <ResourceManager.h>

/**
  Syscall interface.
  This class is abstract, so make it's IF numbers
  quite high to avoid collisions with subclasses.
**/
namespace File_IF
{
  const int identify          =101;
  const int getName           =102;
  const int getImplDefined    =103;
  const int getResource       =104;
}

/**
  These are the function IDs the File will expect to be
  able to use on the resource given.
**/
#define FILE_OPEN   1500
#define FILE_CLOSE  1501
#define FILE_READ   1502
#define FILE_WRITE  1503
#define FILE_LENGTH 1504

/**
  An abstraction on a file. This class provides a common interface
  for methods such as open,close,read,write.

  The public methods are non-virtual and are just trampolines to call
  the private methods _open/_close/etc in their respective owner process.
**/
class File : public Callable
{
public:
  
  typedef u32int ImplDefined;

  /**
    We are a system resource.
  **/
  Resource *resource;
  
  File(char *name, class Filesystem *parent, u32int callbackResourceId, FileTypes type=eFileType_File);
  

  ~File()
  {
    delete resource;
  }
  
  void setImplDefined(u32int a)
  {
    _implDefined = a;
  }
  
  /**
    Used to identify which subclass a particular File* object belongs
    to.
  **/
  FileTypes identify()
  {
    return type;
  }
  
  /**
    Opens a new instance of this file. The file is instantiated
    in the given FileDescriptor.
  **/
  bool open(class FileDescriptor *descriptor);
    
  /**
    Closes the file instance specified by descriptor.
  **/
  bool close(class FileDescriptor *descriptor);
  
  /**
    Read data from the file at a specified offset.
  **/
  u32int read(u32int offset, u32int size, u8int *buffer)
  {
    ASSERT(this);
    return (u32int)callResourceSys(callbackResourceId, FILE_READ, _implDefined, (int)offset, (int)size, (int)buffer);
  }
  
  /**
    Write data to the file at a specified offset.
  **/
  u32int write(u32int offset, u32int size, u8int *buffer)
  {
    ASSERT(this);
    return (u32int)callResourceSys(callbackResourceId, FILE_WRITE, _implDefined, (int)offset, (int)size, (int)buffer);
  }

  /**
  Read data from the file at a specified offset.
   **/
  u32int read(u32int offset, u32int size, u32int bufferResource)
  {
    ASSERT(this);
    Resource *callbackResource = resourceManager.request(callbackResourceId);
    
    ASSERT(callbackResource);
    Resource *bufferRes = resourceManager.request(bufferResource);
    ASSERT(bufferRes);
    
    SharedObject *so = (SharedObject*)bufferRes->getTarget();
    Address addr;
    if (callbackResource->getOwner())
      addr = so->permit( callbackResource->getOwner(), true );
    else
      addr = so->permit( processManager->getProcess()->getPid(), true );
    
    u32int ret = (u32int)callResourceSys(callbackResourceId, FILE_READ, _implDefined, (int)offset, (int)size, (int)addr);
    return ret;
  }
  
  /**
  Write data to the file at a specified offset.
   **/
  u32int write(u32int offset, u32int size, u32int bufferResource)
  {
    ASSERT(this);
    Resource *callbackResource = resourceManager.request(callbackResourceId);
    
    ASSERT(callbackResource);
    Resource *bufferRes = resourceManager.request(bufferResource);
    ASSERT(bufferRes);
    
    SharedObject *so = (SharedObject*)bufferRes->getTarget();
    Address addr;
    if (callbackResource->getOwner())
      addr = so->permit( callbackResource->getOwner(), true );
    else
      addr = so->permit( processManager->getProcess()->getPid(), true );
    
    u32int ret = (u32int)callResourceSys(callbackResourceId, FILE_WRITE, _implDefined, (int)offset, (int)size, (int)addr);
    return ret;
  }
  
  /**
    Get the length of the file.
  **/
  u32int length()
  {
    ASSERT(this);
    return (u32int)callResourceSys(callbackResourceId, FILE_LENGTH, _implDefined);
  }

  /**
    Get the UID of the file
  **/
  u32int getUid()
  {
    return uid;
  }
  
  /**
    Get the GID of the file
  **/
  u32int getGid()
  {
    return gid;
  }
  
  /**
    Get the permissions mask
  **/
  u32int getPermissions()
  {
    return permissions;
  }
  
  u32int getResource()
  {
    return callbackResourceId;
  }
  
  /**
    All set operations are virtual.
  **/
  virtual void setUid(u32int newUid)
  {
    PANIC("setUid() called on pure virtual file!");
  }
  virtual void setGid(u32int newGid)
  {
    PANIC("setGid() called on pure virtual file!");
  }
  virtual void setPermissions(u32int newPermissions)
  {
    PANIC("setPermissions() called on pure virtual file!");
  }
  
  /**
    Get the name of the file.
  **/
  char *getName()
  {
    return name;
  }
  
  /**
    Set the parent directory of this file.
    Should ONLY be called from Directory.h
  **/
  void setParentDir(class Directory *dir)
  {
    parentDir = dir;
  }
  
  u32int getImplDefined()
  {
    return _implDefined;
  }
  
protected:
  ImplDefined _implDefined;
  
  FileTypes type;
  
  /**
    The ID of the resource we should use to fire off open/close commands to.
  **/
  u32int callbackResourceId;
  
  /**
    The version of the file. This gets incremented when either it 
    or any of its direct children change (e.g. for a directory, it
    should only be incremented when one of it's immediate children has
    a filename change, or is added or removed).
  **/
  u32int version;
  
  /**
    Each file must have a name.
  **/
  char *name;
  
  /**
    The owner's UID
  **/
  u32int uid;
  
  /**
    The owner's GID
  **/
  u32int gid;
  
  /**
    The permissions mask
  **/
  u32int permissions;

  /**
     The filesystem this file belongs to.
  **/
  class Filesystem *parent;
  
  /**
    The Directory this file belongs to.
  **/
  class Directory *parentDir;

};

#endif
